package jp.seraph.jsade.core;

import java.io.IOException;
import java.text.ParseException;
import java.util.List;

import jp.seraph.jsade.effector.Effector;
import jp.seraph.jsade.message.MessageUtil;
import jp.seraph.jsade.net.DefaultRoboCupSocket;
import jp.seraph.jsade.perceptor.Perceptor;
import jp.seraph.jsade.sexpression.ExpressionParser;
import jp.seraph.jsade.sexpression.LRParser;
import jp.seraph.jsade.sexpression.Node;
import jp.seraph.jsade.sexpression.SExpression;
import jp.seraph.jsade.sexpression.StringVisitor;
import jp.seraph.jsade.util.PerceptorUtil;

/**
 * AgentRunnerのデフォルト実装です。<br>
 * <br>
 * 与えられたWorldとAgentを使用してサーバへの接続を行います。<br>
 * 接続後は、サーバからのメッセージ受信によりWorldの内部状態を更新し、Agent#thinkメソッドの呼び出しを行います・。
 *
 */
public class DefaultAgentRunner extends Thread implements AgentRunner {
    public DefaultAgentRunner(World aWorld, AgentContext aContext, Agent aAgent) {
        this(aWorld, aContext, aAgent, new LRParser());
        
    }
    
    public DefaultAgentRunner(World aWorld, AgentContext aContext, Agent aAgent, ExpressionParser aParser){
        mWorld = aWorld;
        mContext = aContext;
        mAgent = aAgent;
        mRunning = false;
        mParser = aParser;
    }

    private World mWorld;

    private AgentContext mContext;

    private Agent mAgent;

    private boolean mRunning;
    
    private ExpressionParser mParser;

    /**
     *
     * @see jp.seraph.jsade.core.AgentRunner#stopAgent()
     */

    public void stopAgent() {
        mRunning = false;
    }

    /**
     *
     * @see jp.seraph.jsade.core.AgentRunner#startAgent()
     */

    public void startAgent() {
        this.start();
    }

    /**
     * エージェントの実行を開始します。<br>
     *
     * @see java.lang.Thread#run()
     */
    public void run() {
        if (mRunning)
            throw new IllegalStateException(MessageUtil.alreadyRunning());

        DefaultRoboCupSocket tSocket = null;

        try {
            try {
                tSocket = new DefaultRoboCupSocket();
                tSocket.connect(mContext.getServerAddress());
                tSocket.write(mContext.getModelInitializer());
                tSocket.read(); // 受信を待って読み捨てる
                tSocket.write(this.createInit());
            } catch (IOException e) {
                e.printStackTrace();
                return;
            }
            mRunning = true;

            int tTurnCount = 0;

            while (mRunning) {
                try {
                    tTurnCount++;
                    String tReceive = tSocket.read();

                    List<SExpression> tReceiveExpr = mParser.parse(tReceive);

                    for (SExpression tExpr : tReceiveExpr) {
                        if (tExpr.isNode()) {
                            // パーセプタの生成とワールドへの適用
                            
                            try{
                                Perceptor tPerceptor = PerceptorUtil.createPerceptor((Node) tExpr);
                                tPerceptor.accept(mWorld);
                            }catch(RuntimeException e){
                                e.printStackTrace();
                                System.out.println(tReceive);
                            }
                        }
                    }

                    mWorld.setUpTurn();
                    Player tPlayer = mWorld.getModelManager().createPlayer();
                    mAgent.think(mWorld, tPlayer);
                    List<Effector> tEffectors = tPlayer.buildEffector();
                    StringVisitor tVisitor = new StringVisitor();
                    for (Effector tEffector : tEffectors)
                        tEffector.accept(tVisitor);

                    tSocket.write(tVisitor.getString());
                    mWorld.tearDownTurn();

                } catch (IOException e) {
                    e.printStackTrace();
                    mRunning = false;
                } catch (ParseException e) {
                    e.printStackTrace();
                    mRunning = false;
                }
            }
        } finally {
            if (tSocket != null)
                tSocket.close();
        }
    }

    private String createInit() {
        StringBuilder tBuilder = new StringBuilder();
        tBuilder.append("(init");
        tBuilder.append("(unum ").append(mContext.getUniformNumber()).append(")");
        tBuilder.append("(teamname ").append(mContext.getTeamName()).append(")");
        tBuilder.append(")");

        return tBuilder.toString();
    }
}
